package productions;

import exceptions.ParserException;
import lexico.ErrorTable;
import lexico.SymbolTable;
import lexico.TokenClass;

/**
 * @author MarconeAssis
 */
public class FatorProduction extends AbstractProductionRule {

    private static FatorProduction instance = new FatorProduction();

    public static FatorProduction getInstance() {
        return instance;
    }

    /**
     * Regra: fator : ('!'|op_soma)? ('(' expr ')' | 'NUMERIC' | 'VERDADEIRO' |
     * 'FALSO' | (op_inc)* IDENTIFIER (array)* (chamada_funcao | op_inc*)| cast);
     * @return
     * @throws ParserException
     */
    @Override
    public boolean analyze(SymbolTable st, ErrorTable et) throws ParserException {

        boolean prefix = basicParse(st, TokenClass.OPERATOR_NOT) || basicParse(st, TokenClass.OPERATOR_PLUS);

        if (basicParse(st, TokenClass.DELIMITER_OPENING_PARENTHESIS)) {
            if (!ExprProduction.getInstance().analyze(st, et)) {
                return skipUntilSynchronize(st, et, "Era esperado uma expressão.");
            }
            if (!basicParse(st, TokenClass.DELIMITER_CLOSING_PARENTHESIS)) {
                return skipUntilSynchronize(st, et, "Era esperado um '('.");
            }
            return true;
        }

        if (basicParse(st, TokenClass.NUMERIC) || basicParse(st, TokenClass.NUMERIC_REAL)) {
            return true;
        }
        if (basicParse(st, TokenClass.KEYWORD_VERDADEIRO)) {
            return true;
        }
        if (basicParse(st, TokenClass.KEYWORD_FALSO)) {
            return true;
        }

        int count = 0;
        while (OpIncProduction.getInstance().analyze(st, et)) {
            count++;
        }

        if (basicParse(st, TokenClass.IDENTIFIER)) {
            ChamadaFuncaoProduction.getInstance().analyze(st, et);
            while (OpIncProduction.getInstance().analyze(st, et)) {
            }
            while (ArrayProduction.getInstance().analyze(st, et)) {
            }
            return true;
        } else if (count > 0) {
            return skipUntilSynchronize(st, et, "Era esperado um identificador");
        }

        if (CastProduction.getInstance().analyze(st, et)) {
            return true;
        }

        return false;
    }

    @Override
    protected void populateFirst() {
        first.add(TokenClass.OPERATOR_NOT);
        first.add(TokenClass.DELIMITER_OPENING_PARENTHESIS);
        first.add(TokenClass.NUMERIC);
        first.add(TokenClass.NUMERIC_REAL);
        first.add(TokenClass.KEYWORD_VERDADEIRO);
        first.add(TokenClass.KEYWORD_FALSO);
        first.add(TokenClass.IDENTIFIER);

        //from opsoma first
        this.first.add(TokenClass.OPERATOR_PLUS);
        this.first.add(TokenClass.OPERATOR_MINUS);

        //from opinc first
        first.add(TokenClass.OPERATOR_INCREMENT);
        first.add(TokenClass.OPERATOR_DECREMENT);

        //from cast first
        this.first.add(TokenClass.KEYWORD_CAST);
    }

    protected void populateFollow() {
        //from opmult first
        this.follow.add(TokenClass.OPERATOR_MULT);
        this.follow.add(TokenClass.OPERATOR_DIVISION);

        //from termo follow
        follow.add(TokenClass.DELIMITER_CLOSING_SQUARE_BRACKET);
        this.follow.add(TokenClass.OPERATOR_LESS);
        this.follow.add(TokenClass.OPERATOR_LESS_EQUAL);
        this.follow.add(TokenClass.OPERATOR_MORE);
        this.follow.add(TokenClass.OPERATOR_MORE_EQUAL);
        this.follow.add(TokenClass.OPERATOR_DIFFERENT);
        this.follow.add(TokenClass.OPERATOR_EQUAL);
        follow.add(TokenClass.OPERATOR_AND);
        follow.add(TokenClass.DELIMITER_COMMA);
        follow.add(TokenClass.DELIMITER_CLOSING_PARENTHESIS);
        follow.add(TokenClass.DELIMITER_SEMICOLON);
        follow.add(TokenClass.DELIMITER_CLOSING_CURLY_BRACKET);
        follow.add(TokenClass.DELIMITER_CLOSING_PARENTHESIS);
        follow.add(TokenClass.DELIMITER_SEMICOLON);
        follow.add(TokenClass.OPERATOR_OR);
        this.follow.add(TokenClass.OPERATOR_PLUS);
        this.follow.add(TokenClass.OPERATOR_MINUS);
    }

}
